iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 12
3
Modern Web

Half-Stack Developer 養成計畫系列 第 12

可以幫我自動化嗎,拜託:Gulp

  • 分享至 

  • xImage
  •  

可以幫我自動化嗎,拜託:Gulp

在上一篇當中我們暫時脫離了瀏覽器跟前端,進入到 Node.js 的領域了。在這一篇裡面我們把主題拉回來前端。之前有介紹過 SCSS 跟 Babel,可以把你的 SCSS 還有 ES6 程式碼轉化成瀏覽器看得懂的格式,就可以正常使用了。

在基本功能都正常以後,下一步就是要來優化了。怎麼優化呢?你想想看你的 CSS 跟 JavaScript 是不是有很多可以優化的地方,例如說檔案大小。你可以把所有的空行跟多餘的空白都拿掉,檔案大小就會變小。但程式碼的可讀性也會變得很差,所以記得是要寫到一個新的檔案去,而不是直接更改原來的 source code。

就讓我們先來試試看這個流程吧!我們從無到有把一整個專案 build 起來試試看。

HTML 的話,就讓我們拿之前那個寫簡單的 blog 的 HTML 來用:

<html>
<head>
  <link rel="stylesheet" type="text/css" href="cool.css">
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
  <script src="my.js"></script>
</head>
<body>
  <h1 id="title">This is my blog</h1>
  <div>
    文章標題:<input type="text" name="title" />
  </div>
  <button id="btn">新增</button>
  <div class="posts">

  </div>
  
</body>
</html>

再來的話,用最新最潮的 ES6 語法寫一個 my-es6.js

let count = 1;
$(document).ready(() => {

  $(document).on('click', '.delete', function() {
    $(this).parent().remove();
  })
  $('#btn').click(() => {
    let title = $('input[name=title]').val();
    if(title == '') {
      alert('標題不能空白');
      return;
    }
    $(`<div class="post">
        <h2>${title}</h2>
        <p>這是我的第${count++}篇文章</p>
        <button class="delete">刪除</button>
      </div>`).appendTo('.posts').hide().fadeIn(2000);
  })
})

因為要用 babel,所以記得要把環境先弄好:

npm init
npm install babel-cli --save
npm install babel-preset-latest --save-dev
echo '{"presets": ["latest"]}' > .babelrc

然後來改一下package.json

{
  "name": "hello",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "babel my-es6.js > my.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
  },
  "devDependencies": {
    "babel-cli": "^6.18.0",
    "babel-preset-latest": "^6.16.0"
  },
  "description": ""
}

再來只要npm run build,就會把你的 ES6 compile 然後存成 my.js了。JavaScript 搞定了,接下來來看 CSS 好了,我們一樣照抄之前就寫好的 SCSS:

$title-bg: #c9dacd;
$title-border: #71949e;
$post-bg: #cce8de;

body {
  font-family: '微軟正黑體';
}
#title {
  text-align: center;
  font-size: 40px;
  border: 20px inset $title-border;
  background: $title-bg;
  padding: 15px;
}
.post {
  text-align: center;
  background: $post-bg;
  padding: 30px;
  padding-top: 10px;
  margin-bottom: 15px;
  margin-left: 20px;
  margin-right: 20px;
  border-radius: 10px;

  h2 {
    font-size: 30px;
    color: #333;
  }

  p {
    margin: 0 auto;
    font-size: 16px;
    width: 50%;
    line-height: 18px;
    letter-spacing: 1px;
  }  
}

還記得怎麼 compile 嗎?

sass cool.scss cool.css

CSS 也完成了。於是我們現在該有的檔案都有了,就只剩下壓縮而已。可以直接找線上的工具來幫我們完成這件事:http://www.minifier.org/

CSS 會變成這樣:

@charset "UTF-8";body{font-family:'微軟正黑體'}#title{text-align:center;font-size:40px;border:20px inset #71949e;background:#c9dacd;padding:15px}.post{text-align:center;background:#cce8de;padding:30px;padding-top:10px;margin-bottom:15px;margin-left:20px;margin-right:20px;border-radius:10px}.post h2{font-size:30px;color:#333}.post p{margin:0 auto;font-size:16px;width:50%;line-height:18px;letter-spacing:1px}

JavaScript 會變成這樣:

'use strict';var count=1;$(document).ready(function(){$(document).on('click','.delete',function(){$(this).parent().remove()});$('#btn').click(function(){var title=$('input[name=title]').val();if(title==''){alert('標題不能空白');return}
$('<div class="post">\n        <h2>'+title+'</h2>\n        <p>\u9019\u662F\u6211\u7684\u7B2C'+count++ +'\u7BC7\u6587\u7AE0</p>\n        <button class="delete">\u522A\u9664</button>\n      </div>').appendTo('.posts').hide().fadeIn(2000)})})

空白跟換行基本上都不見了,也因此少掉很多檔案大小。

做到這一步,你就有了檔案很小的 CSS 與 JavaScript 了!但是因為我們的 code 本來就不多,所以能壓縮的幅度也相對的少很多,但是在你真的寫專案的時候,放到 production 上的 code 一定會壓縮,因為檔案大小會少很多很多。

你看看 jQuery 1.12.4,壓縮前是 293 kb,壓縮後是 97 kb,節省了大概 200kb,假設你的網站一天有 1000 次造訪,就節省了大概 200 MB 的流量!這樣累積下來,能夠節省的量十分可觀。而且壓縮過後的檔案傳輸會比較快,所以上線到正式環境的檔案幾乎都會先做過壓縮。

可是現在問題來了,假如說我要改我的 JavaScript 或是 SCSS,改完之後我不就要全部流程再重新做一遍嗎? 如果每一次都要手動 compile,不是有很多指令要打嗎?這樣會不會太麻煩了一點?有沒有什麼更方便的方法?

當然,我可以自己寫 bash script 或是用 node 自己寫一個小程式來跑這些流程,可是如果每一次多一個專案都要再改這些程式碼,這又是另外一件很麻煩的事情了。有沒有什麼標準讓我們可以很方便的處理這些東西呢?有!它就叫做Gulp,slogan 是Automate and enhance your workflow

先來安裝一下 gulp 以及等等會使用到的 gulp plguin

npm install --save-dev gulp
npm install --save-dev gulp-babel

接著建立一個 gulpfile.js

var gulp = require('gulp');
var babel = require('gulp-babel');

gulp.task('default', function() {
  gulp.src('my-es6.js')
      .pipe(babel())
      .pipe(gulp.dest('dist'))
});

然後改一下你的 package.json 的其中一部分

"scripts": {
  "build": "gulp"
},

最後執行熟悉的 npm run build,結束之後你就會看到 dist 資料夾底下多了一個叫做 my-es6.js 的檔案,就是 compile 後的 JavaScript。

好,我們來解釋一下 gulp 到底是幹嘛的,以及到底發生了什麼事情。gulp 它定好了一系列的規則,然後也提供了很多的 API,目的都是讓你能「有系統性、有流程的」去處理很多事情。那他之所以強大就是因為規則都定好了,所以你可以自己幫他寫 plguin,去做你想要做的事情。

以剛剛示範的例子來說,gulp-babel就是別人寫好的插件,你只要照著用就可以達成跟babel my-es6.js一樣的效果。gulp.task就是定義一個任務,後面接任務的名稱跟你要做的事情(一個 function),default 代表預設就會執行到這一個,你要改成其他的也可以。

gulp.src('my-es6.js')你可以想成是把這個檔案給讀進來,然後通過.pipe傳給下一個指令,下一個指令是babel(),就會把你剛讀進來的那些檔案透過 babel compile,再來繼續透過.pipe傳下去,這次是gulp.dest('dist'),就是指定輸出的位置叫做dist這個資料夾,所以你最後的成果就會在這邊被輸出。

如果想重新命名怎麼辦呢?就用一個叫做gulp-rename的 plugin 就好了:

var gulp = require('gulp');
var babel = require('gulp-babel');
var rename = require("gulp-rename");

gulp.task('default', function() {
  gulp.src('my-es6.js')
      .pipe(babel())
      .pipe(rename('my.js'))
      .pipe(gulp.dest('dist'))
});

最後直接來示範一下,用各種 gulp 的 plugin 去打造一個完整的 build 流程


// 這邊就是引入各種 gulp 的 plugin,用法可以自己用關鍵字去查
var gulp = require('gulp');
var babel = require('gulp-babel');
var rename = require("gulp-rename");
var sass = require('gulp-sass');
var gulpSequence = require('gulp-sequence');
var cleanCSS = require('gulp-clean-css');
var uglify = require('gulp-uglify');

// 這個是把 cool.scss compile 成 css,然後寫到 dist 這個資料夾底下
gulp.task('sass', function () {
  return gulp.src('cool.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dist'));
});

// 這個是把 my-es6.js 用 babel compile,重新命名成 my.js 然後存到 dist 底下
gulp.task('babel', function () {
  return gulp.src('my-es6.js')
      .pipe(babel())
      .pipe(rename('my.js'))
      .pipe(gulp.dest('dist'));
})

// 這個是壓縮 CSS,並且重新命名叫 cool.min.css
gulp.task('minify-css', function () {
  return gulp.src('dist/cool.css')
    .pipe(cleanCSS())
    .pipe(rename('cool.min.css'))
    .pipe(gulp.dest('dist'));
})

// 這個是壓縮 js,重新命名成 my.min.js
gulp.task('uglify-js', function () {
  return gulp.src('dist/my.js')
    .pipe(uglify())
    .pipe(rename('my.min.js'))
    .pipe(gulp.dest('dist'));
})

// 這個是為了讓上面的任務有順序的跑
gulp.task('default', gulpSequence('sass', 'babel', 'minify-css', 'uglify-js'));

上面的檔案如果看不懂,你多看幾次找一下規律就會懂了,你會發現怎麼每一個都長得差不多,都是.pipe然後接一個東西,你要讀檔案就用gulp.src,要寫檔案就用gulp.dest,其他事情就去找各種 plugin 用就好了。

用 gulp 的好處就是 plugin 很多,幾乎你想得到的全部都有了。而且我們可以把每個任務都切得很小,方便管理。在最後執行的時候再把每一個任務都串接起來,就是整個 build 的流程了。

我們這一篇之所以最開始先讓你手動跑完這個流程,就是要讓你知道為什麼我們要用 gulp。不是因為潮,是因為每次自己手動 compile 然後貼到壓縮網站上面再自己手動貼上、存檔這個流程實在是太累了,而且很人工,應該要有更好的方式才對。有了 gulp 以後,這種事情都可以寫在 gulp 裡面,你只要簡單地執行一個指令就可以完成所有事項了。

到這一篇為止,你已經可以寫出一個小型的網頁前端專案了,並且利用 gulp 來優化你的 workflow,讓整個編譯的流程體驗更好。前端工程的上半場就到這邊暫時告一個段落了,接下來,要開始後端的部分了!


上一篇
讓我們先轉個 180 度:Node.js 與 npm
下一篇
網頁後端原理:http.createServer
系列文
Half-Stack Developer 養成計畫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言